/******************************************************************************************************
Copyright (C) 2022 Mestrelab Research S.L. All rights reserved.

This file is part of the Mnova scripting toolkit.

Authorized users of MNova Software may use this file freely, but this file is provided AS IS
with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE.
*****************************************************************************************************/

/*global elvis, gc, nmr, mainWindow, qsTr, print, serialization, settings, Application, Dir, Document, ElViSSpectrum, NMRSpectrum, NMRPlugin, FileDialog, File, FileInfo, Env, MessageBox, MnUi, ProgressDialog, TextStream, TSpectraStackImporter, WImportSpectraStackDialog*/
/*jslint plusplus: true, bitwise: true, continue:true, indent: 4*/

function spectraStackImporter() {
	'use strict';

	var settingsKey, auxSettingsKey, importEngine, savedState, importDialog, settingsValue, spc, dialogAnswer;

	spc = "";
	settingsKey = 'Spectra Stack Importer';
	auxSettingsKey = settingsKey;
	importEngine = new TSpectraStackImporter();
	settingsValue = settings.value(settingsKey, "");

	//It keeps compatibility in order to with old versions
	//If exists the last one with data we use it.
	//If it has not data but the previous one exists it takes the data from the previous one but it will not be used any longer
	//In the rest of the cases it uses the last one to read and store the data.
	if (settingsValue === undefined || settingsValue.toString().trim() === "") {
		auxSettingsKey = 'NMR Spectra Stack Importer';
		settingsValue = settings.value(auxSettingsKey, "");
		if (settingsValue === undefined || settingsValue.toString().trim() === "") {
			auxSettingsKey = 'Spectra Stack Importer';
		}
	}

	savedState = JSON.parse(settings.value(auxSettingsKey, JSON.stringify(importEngine)));
	importEngine.copy(savedState);
	importDialog = new WImportSpectraStackDialog(importEngine);
	importDialog.currentDialog(importDialog);
	if (importDialog.exec()) {
		importEngine.copy(importDialog.fParams);
		try {

			//Create the stack
			spc = importEngine.importSpectra();

			//Change the values of the X-Axis if it is needed
			if (spc !== undefined && spc.isValid()) {
				if (importEngine.doImportArrayValues) {
					if (importDialog.importArrayResult !== undefined && importDialog.importArrayResult.data !== undefined && importDialog.importArrayResult.data.length > 0) {
						if (importDialog.importArrayResult.data.length !== spc.specCount) {
							dialogAnswer = MessageBox.question(qsTr("The import array file has different number of values than the spectra stack length. Do you want to import these values anyway?"),  MessageBox.Yes, MessageBox.No, "");
							if (dialogAnswer === MessageBox.Yes) {
								importDialog.updateImportArrayedData(spc, importDialog.importArrayResult.data);
							}
						} else {
							importDialog.updateImportArrayedData(spc, importDialog.importArrayResult.data);
						}
					} else {
						MessageBox.warning(qsTr("An error has occurred importing the array values"));
					}
				}
			} else if (!spc.isValid()) {
				MessageBox.critical(qsTr("The spectra stack is not valid"));
			} else if (spc === undefined) {
				MessageBox.critical(qsTr("The spectra stack has not been created"));
			}
		} catch (er) {
			MessageBox.critical(er.toString());
			return;
		} finally {
			settings.setValue(settingsKey, JSON.stringify(importEngine));
		}
	}
}

/**************************************************************************************************/

function TSpectraType(aType) {
	'use strict';

	this.type = TSpectraType.safeSpectraType(aType);
	this.itemName = TSpectraType.itemName(this.type);
}

TSpectraType.prototype.setType = function (aType) {
	'use strict';

	if (aType !== this.type) {
		this.type = TSpectraType.safeSpectraType(aType);
		this.itemName = TSpectraType.itemName(this.type);
	}
};

TSpectraType.prototype.guessType = function (aPage) {
	'use strict';

	var spectra;

	spectra = aPage.itemsByName(TSpectraType.itemName(TSpectraType.NMR));
	if (spectra.length > 0) {
		this.setType(TSpectraType.NMR);
	} else {
		spectra = aPage.itemsByName(TSpectraType.itemName(TSpectraType.ELVIS));
		if (spectra.length > 0) {
			this.setType(TSpectraType.ELVIS);
		} else {
			this.setType(TSpectraType.UNKNOWN);
		}
	}
};

TSpectraType.prototype.isValid = function () {
	'use strict';

	return TSpectraType.isValidType(this.type);
};

TSpectraType.itemName = function (aType) {
	'use strict';

	if (aType === TSpectraType.NMR) {
		return TSpectraType.nmrSpectrumType;
	}
	if (aType === TSpectraType.ELVIS) {
		return TSpectraType.elvisSpectrumType;
	}
	return undefined;
};

TSpectraType.prototype.newItem = function (aPageItem) {
	'use strict';

	if (this.type === TSpectraType.NMR) {
		return new NMRSpectrum(aPageItem);
	}
	if (this.type === TSpectraType.ELVIS) {
		return new ElViSSpectrum(aPageItem);
	}
	return undefined;
};

TSpectraType.prototype.createArrayedItem = function (aSpectraStack, aView) {
	'use strict';

	if (this.type === TSpectraType.NMR) {
		return nmr.createArrayedItem(aSpectraStack, aView);
	}
	if (this.type === TSpectraType.ELVIS) {
		return elvis.createArrayedItem(aSpectraStack, aView);
	}
	return undefined;
};

TSpectraType.prototype.extractSpectrumFromArrayedItem = function (aArrayedItem, aIndex) {
	'use strict';

	if (this.type === TSpectraType.NMR) {
		return nmr.extractSpectrumFromArrayedItem(aArrayedItem, aIndex);
	}
	if (this.type === TSpectraType.ELVIS) {
		return elvis.extractSpectrumFromArrayedItem(aArrayedItem, aIndex);
	}
	return undefined;
};

TSpectraType.prototype.hideSpectra = function (aArrayedItem, aHiddenIndexes) {
	'use strict';

	if (this.type === TSpectraType.NMR) {
		aArrayedItem.hideSpectra(aHiddenIndexes);
	}
	if (this.type === TSpectraType.ELVIS) {
		print("TODO");
	}
};

TSpectraType.UNKNOWN = 0;
TSpectraType.NMR = 1;
TSpectraType.ELVIS = 2;
TSpectraType.nmrSpectrumType = "NMR Spectrum";
TSpectraType.elvisSpectrumType = "ElViS Spectrum";
TSpectraType.supportedSpectrumTypes = [TSpectraType.nmrSpectrumType, TSpectraType.elvisSpectrumType];

TSpectraType.isValidType = function (aType) {
	'use strict';
	if (aType === TSpectraType.NMR) {
		return true;
	}
	if (aType === TSpectraType.ELVIS) {
		return true;
	}
	return false;
};

TSpectraType.safeSpectraType = function (aType) {
	'use strict';
	if (aType === TSpectraType.NMR) {
		return TSpectraType.NMR;
	}
	if (aType === TSpectraType.ELVIS) {
		return TSpectraType.ELVIS;
	}
	return TSpectraType.UNKNOWN;
};

/**************************************************************************************************/

function TSpectraStackImporter(aCopy) {
	'use strict';

	this.inputDir = Dir.home();
	this.historyFiles = [];
	this.currentFile = 'fid *.fid *.jdx';
	this.defaultSorting = this.SortByName;
	this.defaultView = this.ArrayedView;
	this.defaultPalette = this.DefaultPalette;
	this.visualDecimation = 1;
	this.firstDataChunking = 1;
	this.statusNumberOfChunks = false;
	this.numberOfChunks = 1;
	this.stepDataChunking = 2;
	this.countDataChunking = 1;
	this.parameter = 'Nucleus';
	this.parameterValue = "";
	this.importBunchSize = 10;
	this.doBackup = false;
	this.backupDir = Dir.temp();
	this.enableUndo = false;
	this.filesToBeOpened = [];
	this.resultPageId = undefined;
	this.statusDatasetFilter = false;
	this.fProcessingTemplate = '';
	this.totalImportProcess = 0;
	this.statusProcessingTemplate = false;
	this.statusDataChunking = false;
	this.historyMasks = [];
	this.currentMask = undefined;
	this.metadataParameters = {};
	this.doImportArrayValues = false;
	this.importArrayValuesFile = "";
	this.useHiddenDocument = true;
	this.revisionNumberForHiddenDocumentFeature = 19254;
	this.copy(aCopy);
}

TSpectraStackImporter.prototype = {
	ArrayedView : 0,
	SuperimposedView : 1,

	SortByName : 0,
	SortByDate : 1,

	DefaultPalette: 0,
	GradientPalette: 1,
	HuePalette: 2,


	updateProgressBar : function (aProgressBar, aCurrentValue, aTotalValues, aStartValue, aEndValue) {
		'use strict';

		if (aProgressBar !== undefined) {
			aProgressBar.value = Math.round(aStartValue + ((aCurrentValue / aTotalValues) * (aEndValue - aStartValue)));
			aProgressBar.update();
			mainWindow.processEvents();
		}
	},

	dateCompare : function (aInfo1, aInfo2) {
		'use strict';

		var date1, date2, dir, dir2, fileInfoList, fileInfoList2;

		date1 = aInfo1.lastModified;
		date2 = aInfo2.lastModified;
		if (aInfo1.isDir) { //if directory take the newer file in the directory to compare
			dir = new Dir(aInfo1.absoluteFilePath);
			fileInfoList = dir.entryInfoList("*", Dir.Files, Dir.Time);
			if (fileInfoList.length !== 0) {
				date1 = fileInfoList.shift().lastModified;
			}
		}

		if (aInfo2.isDir) {
			dir2 = new Dir(aInfo2.absoluteFilePath);
			fileInfoList2 = dir2.entryInfoList("*", Dir.Files, Dir.Time);
			if (fileInfoList2.length !== 0) {
				date2 = fileInfoList2.shift().lastModified;
			}
		}

		//Sort ascending to have the older first
		if (date1 > date2) {
			return 1;
		}
		if (date1 < date2) {
			return -1;
		}

		return 0;
	},

	toJSON : function () {
		'use strict';

		var jsonState, retObj, i;

		jsonState = ['inputDir', 'historyFiles', 'currentFile', 'defaultSorting', 'defaultView', 'defaultPalette', 'visualDecimation', 'firstDataChunking', 'statusNumberOfChunks', 'numberOfChunks', 'stepDataChunking', 'countDataChunking', 'parameter', 'parameterValue', 'importBunchSize', 'doBackup', 'backupDir', 'enableUndo', 'fProcessingTemplate', 'statusDatasetFilter', 'statusDataChunking', 'statusProcessingTemplate', 'historyMasks', 'currentMask', 'doImportArrayValues', 'importArrayValuesFile'];
		retObj = {};
		for (i = 0; i < jsonState.length; ++i) {
			retObj[jsonState[i]] = this[jsonState[i]];
		}

		return retObj;
	},

	copy : function (aCopy) {
		'use strict';
		var prop;
		if (aCopy !== undefined) {
			for (prop in this) {
				if (this.hasOwnProperty(prop) && aCopy.hasOwnProperty(prop)) {
					this[prop] = aCopy[prop];
				}
			}
		}
	},

	getRelativePath: function (aPath, aArrayPaths) {
		'use strict';

		var i, result, aux;

		result = [];
		for (i = 0; i < aArrayPaths.length; i++) {
			aux = aArrayPaths[i].replace(/\\/g, "/");
			if (aPath.length < aux.length) {
				result.push(aux.substring(aPath.length, aux.length));
			}
		}

		return result;
	},

	getAllFiles : function (aPathStartDirectory, aFileMask, aStackArray, aProgressDialog) {
		'use strict';

		var i, j, dir, dirs, fileInfo, filters, regex, flagEnd, currentFile, currentFilter, auxFilter;

		if (aStackArray === undefined) {
			aStackArray = [];
		}
		fileInfo = new FileInfo(aPathStartDirectory);
		if (fileInfo.isFile) {
			if (aFileMask !== undefined && aFileMask !== "") {
				filters = aFileMask.split(" ");
				for (j = 0; j < filters.length && !aProgressDialog.wasCanceled; j++) {

					//Get the folders bewteen base directory and file
					flagEnd = false;
					currentFile = fileInfo.fileName;
					currentFilter = filters[j];

					//Check if contains a symbol which means will be considered as a regex
					if (!(currentFilter.indexOf("*") > -1 || currentFilter.indexOf("?") > -1)) {
						if (currentFilter === currentFile) {
							aStackArray.push(aPathStartDirectory.toString().replace(/\\/g, "/"));
							break;
						}
					} else {

						//Adapt wildcards symbols to valid regex expresions if is a real regex
						auxFilter = currentFilter.replace(/\./g, "\\.");
						auxFilter = auxFilter.replace(/\*/g, ".*");
						auxFilter = auxFilter.replace(/\?/g, "\\S{1,1}");

						//* symbol management
						if (currentFilter[currentFilter.length - 1] !== "*" && currentFilter[currentFilter.length - 1] !== "?") {
							if (currentFilter.toString().indexOf("*") > -1) {
								auxFilter += "$";
								flagEnd = true;
							}
						}

						//? symbol management
						if (currentFilter.toString().indexOf("?") > -1) {
							auxFilter = "^" + auxFilter;
							if (!flagEnd) {
								auxFilter += "$";
							}
						}

						//Test Regex and stop process if we found it in order to load each file once
						regex = new RegExp(auxFilter);
						if (regex.test(currentFile)) {
							aStackArray.push(aPathStartDirectory.toString().replace(/\\/g, "/"));
							break;
						}
					}
				}
			}
		}

		//Check if we must abort the process
		if (aProgressDialog.wasCanceled) {
			return;
		}
		mainWindow.processEvents();

		//Dir.NoDotAndDotDot is quite important to avoid enter into a endless loop
		dir = new Dir(aPathStartDirectory);
		dirs = dir.entryList("*", Dir.All | Dir.NoDotAndDotDot, Dir.Time);
		for (i = 0; i < dirs.length; i++ && !aProgressDialog.wasCanceled) {
			this.getAllFiles(aPathStartDirectory + "/" + dirs[i], aFileMask, aStackArray, aProgressDialog);
		}

		return aStackArray;
	},

	getFilesFiltered: function (aArrayPaths, aFilters) {
		'use strict';

		var i, j, filters, result, regex, auxFilter, currentFolder, currentFilter, auxString, flagEnd;

		if (aFilters !== undefined && aFilters !== "") {
			filters = aFilters.split(" ");
			result = [];
			for (i = 0; i < aArrayPaths.length; i++) {
				for (j = 0; j < filters.length; j++) {

					//Get the folders bewteen base directory and file
					flagEnd = false;
					auxString =  aArrayPaths[i].toString().substring(1, aArrayPaths[i].length - 1);
					currentFolder = auxString.substring(0, auxString.lastIndexOf('/'));

					//Adapt wildcards symbols to valid regex expresions
					currentFilter = filters[j];
					auxFilter = currentFilter.replace(/\./g, "\\.");
					auxFilter = auxFilter.replace(/\*/g, ".*");
					auxFilter = auxFilter.replace(/\?/g, "\\S{1,1}");

					//* symbol management
					if (currentFilter[currentFilter.length - 1] !== "*" && currentFilter[currentFilter.length - 1] !== "?") {
						if (currentFilter.toString().indexOf("*") > -1) {
							auxFilter += "$";
							flagEnd = true;
						}
					}

					//? symbol management
					if (currentFilter.toString().indexOf("?") > -1) {
						auxFilter = "^" + auxFilter;
						if (!flagEnd) {
							auxFilter += "$";
						}
					}

					//Test Regex
					regex = new RegExp(auxFilter);
					if (regex.test(currentFolder)) {
						result.push(aArrayPaths[i]);
						break;
					}
				}
			}
		} else {
			return aArrayPaths;
		}

		return result;
	},

	getFilesToImport : function (aSearchData, aProgressDialog, aKeepProgressBarVisible) {
		'use strict';

		var i, dir, files, result, progressDialog;

		//Update instance attributes by parameter
		if (aSearchData !== undefined) {
			this.inputDir = aSearchData.dir;
			this.currentFile = aSearchData.file;
			this.currentMask = aSearchData.mask;
			this.defaultSorting = aSearchData.order;
		}

		files = [];
		if (!(this.inputDir[this.inputDir.length - 1] === "\\" && this.inputDir[this.inputDir.length - 2] === ":")) {
			this.inputDir = this.inputDir.replace(/\\+$|\/+$/, "");
		}
		dir = new Dir(this.inputDir);
		if (dir.exists) {

			//Prepare a progress dialog because sometimes the search files processing could delay too much.
			if (aProgressDialog !== undefined) {
				progressDialog = aProgressDialog;
			} else {
				progressDialog = new ProgressDialog();
			}
			progressDialog.labelText = "Getting data from files ...";
			progressDialog.visible = true;

			//Get all files already filtered by the file name masks
			files = this.getAllFiles(this.inputDir, this.currentFile, undefined, progressDialog);

			//Check if user want to cancel
			if (progressDialog.wasCanceled) {
				return;
			}

			//We will apply the folder name keywords only in folders below to the inputDir related to the file hierarchy
			files = this.getRelativePath(this.inputDir, files);

			//Get final files filteres by the folder name keyWords
			if (this.currentMask !== undefined && this.currentMask !== "") {
				files = this.getFilesFiltered(files, this.currentMask);
			}

			//Get files absolute path
			result = [];
			for (i = 0; i < files.length; i++) {
				result.push(new FileInfo(this.inputDir + files[i]));
			}

			//Sort files by using the proper order
			this.filesToBeOpened = [];
			if (this.defaultSorting === this.SortByName) {
				this.filesToBeOpened = result.sort(Dir.alphanumericPathsCompare);
			} else if (this.defaultSorting === this.SortByDate) {
				this.filesToBeOpened = result.sort(this.dateCompare);
			}

			//Keep progress bar visible if is needed
			if (aKeepProgressBarVisible === undefined || !aKeepProgressBarVisible) {
				progressDialog.visible = false;
			}
		}
	},

	createSpectraStack : function (aSpectrum, aStack) {
		'use strict';

		var spectraStack;

		if (aStack === undefined) {
			spectraStack = [];
		} else {
			spectraStack = aStack;
		}

		if (aSpectrum.isValid()) {
			spectraStack.push(aSpectrum);
			this.totalSpectraToImportProcessed++;
		}

		return spectraStack;
	},

	existArrayedItem : function (aDocument, aSpectraType) {
		'use strict';

		var i, currentSpectrum;

		for (i = 0; i < aDocument.pageItems.length; i++) {
			if (aDocument.pageItems[i].name === aSpectraType.itemName) {
				currentSpectrum = aSpectraType.newItem(aDocument.pageItems[1]);
				if (currentSpectrum.specCount > 1) {
					return true;
				}
			}
		}

		return false;
	},

	createArrayedItem : function (aSpectraStack, aView, aPreviousStackArrayed, aDocument, aSpectraType) {
		'use strict';

		var newArrayedItem, auxPage, i, found, pages, view;

		//Thanks to this first part of the function, we avoid using these prior
		//instructions which were used on the previous version of the script
		//Application.mainWindow.doAction("nmrArrayedStacked");
		//Application.mainWindow.doAction("nmrArrayedOverlayed");
		found = false;
		if (aSpectraStack.length < 2 && this.spectraStack === undefined) {
			if (!this.existArrayedItem(aDocument, aSpectraType)) {
				this.spectraStack = aSpectraStack;
				return;
			}
		} else if (this.spectraStack !== undefined && this.spectraStack.length > 0) {
			for (i = 0; i < this.spectraStack.length; i++) {
				aSpectraStack.unshift(this.spectraStack[i]);
			}
			found = true;
			this.spectraStack = undefined;
		}

		if (aView !== undefined) {
			view = aView;
		}

		newArrayedItem = aSpectraType.createArrayedItem(aSpectraStack, view);
		newArrayedItem.update();
		auxPage = aDocument.curPage().uuid;

		//If we have remain pages due to the last bunch size was 1 we delete the pages at this point.
		if (found) {
			pages = [];
			for (i = 0; i < aSpectraStack.length; i++) {
				pages.push(aSpectraStack[i].page);
			}
			aDocument.deletePages(pages);
		}

		//Thanks to this last part of the function we avoid using these prior
		//instructions which were used on the previous version of the script
		//Application.mainWindow.doAction("nmrArrayedFromSelection");
		//Application.mainWindow.doAction("nmrSuperimposedFromSelection");
		if (aPreviousStackArrayed !== undefined) {
			aPreviousStackArrayed.appendSpectrum(newArrayedItem);
			aPreviousStackArrayed.update();
			aDocument.deletePages(auxPage);

			return aPreviousStackArrayed;
		}

		return newArrayedItem;
	},

	dataChunkingFiltering : function (aArrayOfFiles) {
		'use strict';

		var i, currentElement, itemsPerChunkCounter, stepCounter, chunkCounter,
			files, firstFile, chunkSize, numberOfChunks, step;

		//Check if the data filtering is needed
		if (this.statusDataChunking) {

			//Initialize vars
			firstFile = this.firstDataChunking - 1;
			chunkSize = this.countDataChunking;
			numberOfChunks = this.numberOfChunks;
			step = this.stepDataChunking;
			itemsPerChunkCounter = 0;
			stepCounter = 1;
			chunkCounter = 0;
			files = [];

			//Apply data chuking assuming that each file will contain a spectrum
			for (i = 0; i < aArrayOfFiles.length; i++) {
				currentElement = aArrayOfFiles[i];
				if (i >= firstFile) {
					if (chunkCounter < numberOfChunks || !this.statusNumberOfChunks) {
						if (itemsPerChunkCounter < chunkSize) {
							files.push(currentElement);
							stepCounter++;
							itemsPerChunkCounter++;
						} else if (stepCounter < step) {
							stepCounter++;
						} else {
							chunkCounter++;
							stepCounter = 1;
							itemsPerChunkCounter = 0;
						}
					}
				}
			}
		} else {
			files = aArrayOfFiles;
		}

		return files;
	},

	metadataFiltering : function (aMetadata, aParameter, aValue) {
		'use strict';

		var result, metadata, parameter, value;

		//Initialize vars
		result = false;
		metadata = aMetadata;

		//Get the real parameter name
		parameter = this.metadataParameters[aParameter].parameter;
		value = metadata[parameter];

		//For the specific case of nucleus we need an extra action
		if (Object.keys(metadata).length > 0 && value !== undefined) {
			if (parameter === "nucleus") {
				if (metadata.nD > 1) {
					value = metadata[parameter][1] + "," + metadata[parameter][2];
				} else {
					value = metadata[parameter][1];
				}
			}

			//We evaluate the condition
			result = this.spectralDataFiltering(undefined, aParameter, aValue, value);
		} else {
			result = -1;
		}

		return result;
	},

	spectralMetadataFiltering : function (aArrayOfFiles, aParameter, aValue) {
		'use strict';

		var i, currentElement, currentMetadata, files, flagNotFilter;

		//Check if the data filtering is needed
		if (this.statusDatasetFilter) {

			//Initialize vars
			files = [];

			//Apply data chucking assuming that each file will contain a spectrum
			for (i = 0; i < aArrayOfFiles.length; i++) {
				currentElement = aArrayOfFiles[i];
				currentMetadata = serialization.metaData(currentElement);
				flagNotFilter = this.metadataFiltering(currentMetadata, aParameter, aValue);
				if (flagNotFilter === true) {
					files.push(currentElement);
				}
			}
		} else {
			files = aArrayOfFiles;
		}

		return files;
	},

	arrayIncludesElement : function (aArray, aElement) {
		'use strict';

		var i, result, element;

		result = false;

		if (aArray !== undefined) {
			if (typeof (aArray) === "object") {
				if (aArray.length > 0) {
					if (aElement !== undefined) {
						for (i = 0; i < aArray.length; i++) {
							element = aArray[i];
							if (element === aElement) {
								result = true;
								break;
							}
						}
					}
				}
			}
		}

		return result;
	},

	importSpectra : function (aBackupBasename) {
		'use strict';

		/*jslint evil: true*/

		var oldEnableUndo,
			newDoc,
			resultsDoc,
			filesArr,
			filesArraDataChunking,
			filesArraSpectralMetadata,
			backupFileName,
			auxStatusDataChunking,
			auxStatusDatasetFilter,
			oldFileHistory,
			spc,
			specCount,
			hiddenSpectraIndexes,
			i,
			oldProcEnv,
			code,
			totalSpectraToImport,
			totalSpectraToImportNeeded,
			numberOfFullBunches,
			lastBunchSize,
			totalImportBunchSizeCalled,
			currentBunchSize,
			resultBunchOfSpectra,
			firstSpectrum,
			progressDialog,
			totalFiles,
			totalBunchesProcessed,
			totalFilesProcessed,
			spectra;

		//Initialize progress bar
		progressDialog = new ProgressDialog();
		// progressDialog = {};
		// progressDialog.update = function() {}
		progressDialog.visible = true;

		this.getFilesToImport(undefined, progressDialog, true);
		this.spectrumPerFile = 0;
		this.dimension = 0;

		this.totalSpectra = 0;
		this.totalSpectraProcessed = 0;
		this.totalSpectraToImportProcessed = 0;
		this.spectraType = new TSpectraType();

		totalSpectraToImport = -1;
		currentBunchSize = this.importBunchSize;

		//Try to fit the bunch size to the total of spectra to import
		if (this.statusDataChunking && this.statusNumberOfChunks) {
			totalSpectraToImport = this.countDataChunking * this.numberOfChunks;
			totalSpectraToImportNeeded = (this.numberOfChunks * this.stepDataChunking) - (this.stepDataChunking - this.countDataChunking);
			numberOfFullBunches = Math.floor(totalSpectraToImportNeeded / this.importBunchSize);
			lastBunchSize = totalSpectraToImportNeeded % this.importBunchSize;
			totalImportBunchSizeCalled = 0;
		}

		if (this.filesToBeOpened.length === 0) {
			throw new Error("No files found on path.");
		}

		oldEnableUndo = Application.mainWindow.enableUndo;
		Application.mainWindow.enableUndo = this.enableUndo;
		oldProcEnv = Env.DefaultNMRProcessingSetup; //backup environment

		//Keep backwards compatibility
		if (Application.version.revision < this.revisionNumberForHiddenDocumentFeature) {
			this.useHiddenDocument = false;
		}

		//Create new document
		if (this.useHiddenDocument) {

			//Reuse active document if possible to keep old workflow previous performance improvement in #20587
			if (Application.mainWindow.activeDocument !== undefined && Application.mainWindow.activeDocument.itemCount() === 0) {
				resultsDoc = Application.mainWindow.activeDocument;
			}

			newDoc = new Document();
			Application.lockDocument(newDoc);

		} else {
			newDoc = undefined;
			if (Application.mainWindow.activeDocument !== undefined && Application.mainWindow.activeDocument.itemCount() === 0) {
				newDoc = Application.mainWindow.activeDocument;
			}
			if (newDoc === undefined || !newDoc.isValid() || newDoc.itemCount() !== 0) {
				newDoc = Application.mainWindow.newDocument();
				mainWindow.activeDocument = newDoc;
			}
		}

		filesArr = [];
		for (i = 0; i < this.filesToBeOpened.length; ++i) {
			filesArr.push(this.filesToBeOpened[i].absoluteFilePath);
		}

		//Spectra metadata filtering available only for the NMR datasets
		//The issue #12515 is an stopper due to the metaData method always returns nothing for ElViS datasets
		filesArraSpectralMetadata = this.spectralMetadataFiltering(filesArr, this.parameter, this.parameterValue);
		if (filesArraSpectralMetadata === undefined) {
			return;
		}

		//Data chunking assuming a 1:1 cardinality between spectra and files (we assume one spectrum per file)
		filesArraDataChunking = this.dataChunkingFiltering(filesArraSpectralMetadata);

		//Get the filtered files if so
		filesArr = filesArraDataChunking;

		//Given that we have already performed the filters we set theirs flag off
		auxStatusDataChunking = this.statusDataChunking;
		auxStatusDatasetFilter = this.statusDatasetFilter;
		this.statusDataChunking = false;
		this.statusDatasetFilter = false;

		//setup env
		code = "";
		switch (this.statusProcessingTemplate) {
		case true:
			code = "Env.DefaultNMRProcessingSetup = function (aSpcInfo) {	if(this.open('%1')==false) MessageBox.critical('Failed to open processing template') }".arg(this.fProcessingTemplate);
			break;
		default:
			break;
		}
		if (code.length !== 0) {
			//print(code)
			try {
				eval(code);
			} catch (err) {//in case of error keep the old environment
				print(err);
				Env.DefaultNMRProcessingSetup = oldProcEnv;
			}
		}

		//import files by bunches of specified size
		backupFileName = '';
		if (this.doBackup) {
			backupFileName = '%1/%2_bak.mnova';
			if (aBackupBasename !== undefined) {
				backupFileName = backupFileName.arg(this.backupDir).arg(aBackupBasename);
			} else {
				backupFileName = backupFileName.arg(this.backupDir).arg('importStack');
			}
		}

		//Do not add the imported files to the file history
		oldFileHistory = serialization.fileHistory;
		serialization.fileHistory = false;

		//Progress bar data management
		progressDialog.value = 0;
		totalFiles = filesArr.length;
		totalBunchesProcessed = 0;

		//Data chunking initialization
		this.spectraStackItem = undefined;
		this.currentCountDataChunking = this.countDataChunking;
		this.currentStepDataChunking = this.stepDataChunking;

		//We crate the stack by using bunch of spectra to avoid memory problems
		while (filesArr.length !== 0 && (progressDialog === undefined || !progressDialog.wasCanceled)) {
			//In order to be more efficient we stop the process if we have reached the total of spectra to be processed
			if (totalSpectraToImport > -1) {
				if (totalSpectraToImport <= this.totalSpectraToImportProcessed) {
					break;
				} else {
					if (totalImportBunchSizeCalled < numberOfFullBunches) {
						currentBunchSize = this.importBunchSize;
					} else {
						currentBunchSize = lastBunchSize;
					}
				}
				totalImportBunchSizeCalled++;
			}

			//Update progress bar information
			totalFilesProcessed = totalBunchesProcessed * this.importBunchSize;
			progressDialog.labelText = qsTr("Processing file {0} of {1}... Total stacked spectra ({2})").format(totalFilesProcessed, totalFiles, this.totalSpectraToImportProcessed);

			this.updateProgressBar(progressDialog, totalFilesProcessed, totalFiles, 0, 100);

			//Process bunch of spectra. If the output si wrong we stop the import process
			resultBunchOfSpectra = this.importBunchOfSpectra(newDoc, filesArr.splice(0, currentBunchSize), backupFileName, this.spectraType, this.useHiddenDocument);
			if (resultBunchOfSpectra !== undefined && !resultBunchOfSpectra) {
				return;
			}
			totalBunchesProcessed++;
		}

		//Restore the real value to save them to the settings properly
		this.statusDataChunking = auxStatusDataChunking;
		this.statusDatasetFilter = auxStatusDatasetFilter;

		serialization.fileHistory = oldFileHistory;
		if (this.spectraType.itemName !== undefined && this.spectraType.itemName !== "") {
			firstSpectrum = mainWindow.activeDocument.curPage().itemsByName(this.spectraType.itemName)[0];
			spc = this.spectraType.newItem(firstSpectrum);
			if (spc.isValid()) {

				//Visual decimation
				specCount = spc.specCount;
				hiddenSpectraIndexes = [];
				for (i = 0; i < specCount; ++i) {
					if (i % this.visualDecimation) {
						hiddenSpectraIndexes.push(i);
					}
				}
				if (hiddenSpectraIndexes.length) {
					this.spectraType.hideSpectra(spc, hiddenSpectraIndexes);
					spc.update();
					Application.mainWindow.activeDocument.update();
				}

				//Update view to perform a full out zoom in order to view the whole spectra stack
				spc.zoom();
				spc.update();
				mainWindow.activeDocument.update();
			}
		}

		//Close progressBar
		progressDialog.value = 100;
		progressDialog.visible = false;
		progressDialog = undefined;

		//Bring the result to a visible document
		if (this.useHiddenDocument) {
			Application.unlockDocument();
			if (resultsDoc === undefined || resultsDoc.uuid === undefined || !resultsDoc.isValid()) {
				resultsDoc = mainWindow.newDocument();
			}

			//Translate hidden document content to the visible one
			resultsDoc.deletePages(resultsDoc.pages());
			resultsDoc.clonePages(newDoc.pages());
			mainWindow.activeDocument = resultsDoc;

			//Delete hidden document and return spectrum from the visible one
			if (resultsDoc !== undefined) {
				if (this.spectraType !== undefined) {
					if (this.spectraType.itemName !== undefined) {
						if (this.arrayIncludesElement(TSpectraType.supportedSpectrumTypes, this.spectraType.itemName)) {
							spectra = resultsDoc.itemsByName(this.spectraType.itemName);
							if (spectra.length > 0) {
								spc = spectra[0];
							}
						}
					}
				}
				//Free memory used to build the spectra using hidden document for better performance (speed)
				newDoc.destroy();
				gc();
			}
		}

		Application.mainWindow.enableUndo = oldEnableUndo;
		Env.DefaultNMRProcessingSetup = oldProcEnv;
		if (spc === undefined || !spc.isValid()) {
			throw new Error("No spectrum found with the selected characteristics.");
		}

		return spc;
	},

	manageHiddenDocuments : function (aDocument, aMnovaPath, aItemType, aDeletePage) {
		'use strict';

		var pages, lastPage, auxDoc;

		//Delete the last page that arises as a consequence of the failure of the importFile method
		if (aDeletePage !== undefined) {
			if (aDeletePage) {
				pages = aDocument.pages();
				lastPage = pages[pages.length - 1];
				aDocument.deletePages(lastPage);
			}
		}

		//Create a new document with the content of the mnova file
		auxDoc = new Document(aMnovaPath);

		//Move the pages from the aux document to the current one
		if (auxDoc.pages().length > 0) {
			if (auxDoc.itemCount() > 0) {
				if (aItemType !== undefined && aItemType !== "") {
					if (auxDoc.itemCount(aItemType) > 0) {
						aDocument.clonePages(auxDoc.pages());
					}
				} else {
					aDocument.clonePages(auxDoc.pages());
				}
			}
		}

		//Delete the aux document and free memory
		auxDoc.destroy();
		gc();

	},

	manageVisibleDocuments : function (aOldDocs, aDoc) {
		'use strict';

		var i, j, found, currentDocument;

		if (aOldDocs.length !== mainWindow.documents().length) {
			if (aDoc.pageItems.length === 0) {
				aDoc.deletePages(aDoc.pages());
			}

			//Get the difference among the two arrays
			for (i = 0; i < mainWindow.documents().length; i++) {
				currentDocument = mainWindow.documents()[i];
				found = false;
				if (currentDocument.uuid !== aDoc.uuid) {
					for (j = 0; j < aOldDocs.length; j++) {
						if (currentDocument.uuid === aOldDocs[j].uuid) {
							found = true;
							break;
						}
					}
					if (!found) {
						aDoc.clonePages(currentDocument.pages());
						i--;
						currentDocument.close();
					}
				}
			}
			mainWindow.activeDocument = aDoc;
		}
	},

	getPositionArrayByUuid: function (aArray, aUUID, aType) {
		'use strict';

		var i;

		for (i = 0; i < aArray.length; i++) {
			if (aType === 0) {
				if (aArray[i] === aUUID) {
					return i;
				}
			} else if (aType === 1) {
				if (aArray[i].uuid === aUUID) {
					return i;
				}
			}
		}
	},

	countDecimals : function (aValue) {
		'use strict';

		var output;

		if (isNaN(aValue)) {
			output = -1;
		} else if (aValue.toString().split(".").length === 2) {
			output = aValue.toString().split(".")[1].length;
		} else {
			output = 0;
		}

		return output;
	},

	spectralDataFiltering : function (aNMRSpectrum, aParameter, aParameterValue, aValue) {
		'use strict';
		var value, parameterValue, decimals, output;

		if (aValue !== undefined) {
			value = aValue;
		} else {
			value = aNMRSpectrum.getParam(aParameter).toString().replace(/\[/g, "").replace(/\]/g, "").replace(/ /g, "");
		}
		parameterValue = aParameterValue.replace(/\[/g, "").replace(/\]/g, "").replace(/ /g, "");

		if (!isNaN(parameterValue)) {
			decimals = this.countDecimals(aParameterValue);
			if (decimals > 0) {
				value = Number(value).toFixed(decimals).toString();
				parameterValue = Number(parameterValue).toFixed(decimals).toString();
			}
		}

		if (aValue !== undefined) {
			output = (value.toString() === parameterValue.toString());
		} else {
			output = (!(aNMRSpectrum.isValid() && (value === parameterValue)));
		}

		return output;
	},

	manageLast2Spectrum : function (aArrayedItem, aValidPagesArray, aNotValidPagesArray, aSpectraStack, aIndexSpectrum, aStatus2Spectra, aFlagPreviousExecution, aType, aSpectraType) {
		'use strict';

		var i, spc, filtered;

		//Check if is the first time we manage the last two spectra for the current arrayed item
		if (!aFlagPreviousExecution) {
			for (i = 0; i < aArrayedItem.specCount; i++) {

				//We consider all spectra as good by default
				/*jslint evil: true */
				spc = aSpectraType.extractSpectrumFromArrayedItem(aArrayedItem, i);
				/*jslint evil: false */
				aSpectraStack = this.createSpectraStack(spc, aSpectraStack);
				aValidPagesArray.push(spc.page.uuid);

				//Save data about spectra
				aStatus2Spectra.push({});
				aStatus2Spectra[i].spectrum = spc;
				aStatus2Spectra[i].position = i;
				aStatus2Spectra[i].status = 1;
			}
		}

		//Get current spectrum
		filtered = false;
		spc = {};
		spc = aStatus2Spectra[aIndexSpectrum];

		//Filter data
		if (aType === 0 && this.statusDatasetFilter) {

			//Filter by parameter
			if (this.spectralDataFiltering(spc.spectrum, this.parameter, this.parameterValue)) {
				filtered = true;
			}

			//Remove the spectrum
			if (filtered) {
				aNotValidPagesArray.push(spc.spectrum.page.uuid);
				aValidPagesArray.splice(this.getPositionArrayByUuid(aValidPagesArray, spc.spectrum.page.uuid, 0), 1);
				aSpectraStack.splice(this.getPositionArrayByUuid(aSpectraStack, spc.spectrum.uuid, 1), 1);
				aStatus2Spectra[aIndexSpectrum].status = 0;
			}

		//Delete the current spectrum
		} else if (aType === 1) {

			//If is enabled we deleted it and set to disabled
			if (spc.status === 1) {
				aNotValidPagesArray.push(spc.spectrum.page.uuid);
				aValidPagesArray.splice(this.getPositionArrayByUuid(aValidPagesArray, spc.spectrum.page.uuid, 0), 1);
				aSpectraStack.splice(this.getPositionArrayByUuid(aSpectraStack, spc.spectrum.uuid, 1), 1);
				aStatus2Spectra[aIndexSpectrum].status = 0;
			}
		}
	},

	manageSpectraType: function () {
		'use strict';

		var currentPage;

		currentPage = mainWindow.activeDocument.curPage();
		this.spectraType.guessType(currentPage);
	},

	importBunchOfSpectra : function (aDoc, aAbsPathArr, aBackupFileName, aSpectraType, aUseHiddenDocument) {
		'use strict';

		var currentDocs, currentPages, totalPages, addedPagesIds, index, itemSpc, spcPagesIds, notValidPagesIds,
			isValidPage, curPage, typeItems, i, j, k, spectraStack, chunked, filtered, flag2Spectrum, totalChunks,
			twoSpectrumStatus, result, isRawData, deletePage;

		//Initialize output flag
		result = true;

		//Hidden document opening management
		if (aUseHiddenDocument) {

			//Open all spectra and get all pages
			for (i = 0; i < aAbsPathArr.length; i++) {

				//Check if the data to be imported is raw data because the importFile method only works with raw data
				//We determine the difference in pages as it will be very important when the dataset consists of a mnova document
				currentPages = aDoc.pages().length;
				isRawData = serialization.importFile(aAbsPathArr[i], "", aDoc);
				totalPages = aDoc.pages().length;

				//In this case, we assume that the data set is actually an mnova file. 
				if (!isRawData) {

					//Support for .mnova files. Even is possible to mix them with another kind or themselves
					deletePage = currentPages !== totalPages;
					this.manageHiddenDocuments(aDoc, aAbsPathArr[i], aSpectraType.itemName, deletePage);
				}

				//Determine the spectra type used, i.e. NMR, ElViS
				if (!aSpectraType.isValid()) {
					this.manageSpectraType();
				}
			}

			if (!aSpectraType.isValid()) {
				MessageBox.critical("Unknown type of spectra detected");
				return result;
			}

		//Visible document opening management
		} else {

			currentDocs = mainWindow.documents();
			for (i = 0; i < aAbsPathArr.length; i++) {
				serialization.open(aAbsPathArr[i]);
				if (!aSpectraType.isValid()) {
					this.manageSpectraType();
				}
			}

			if (!aSpectraType.isValid()) {
				MessageBox.critical("Unknown type of spectra detected");
				return result;
			}

			//Support for .mnova files. Even is possible to mix them with another kind or themselves
			this.manageVisibleDocuments(currentDocs, aDoc);
		}

		//Initialize selection of pages to work with
		aDoc.setSelection([]);
		addedPagesIds = aDoc.pagesIds();

		//Remove the stack of the array. This stack was created before (a previous bunch of spectra)
		if (this.resultPageId !== undefined) {
			index = addedPagesIds.indexOf(this.resultPageId.uuid);
			addedPagesIds.splice(index, 1);
		}

		spcPagesIds = [];
		notValidPagesIds = [];
		spectraStack = [];
		isValidPage = false;

		//Remove pages without valid spectrum parameter
		for (i = 0; i < addedPagesIds.length; ++i) {
			curPage = aDoc.page(addedPagesIds[i]);
			isValidPage = false;

			if (curPage.isValid()) {
				typeItems = curPage.itemsByName(aSpectraType.itemName);
				isValidPage = typeItems.length !== 0;

				itemSpc = aSpectraType.newItem(typeItems[0]);
				this.spectrumPerFile = aSpectraType.newItem(typeItems[0]).specCount;

				//Get the number of the dimension by getting the first spectrum
				if (this.dimension === 0) {
					this.dimension = itemSpc.dimCount;
				}

				//Check if the user doesn't mix dimensions
				if (this.dimension !== itemSpc.dimCount) {
					isValidPage = false;
				}

				//****Manage Single spectrum per file******
				if (this.spectrumPerFile < 2 && isValidPage) {

					if (this.statusDatasetFilter) {

						//Filter by parameter
						if (this.spectralDataFiltering(itemSpc, this.parameter, this.parameterValue)) {
							isValidPage = false;
						}
					}

					//Interval Management. We count every spectrum that has not been filtered
					if (isValidPage && this.statusDataChunking) {
						totalChunks =  this.numberOfChunks * this.stepDataChunking;
						this.totalSpectra++;
						if (this.totalSpectra < this.firstDataChunking || ((this.totalSpectraProcessed >= totalChunks) && this.statusNumberOfChunks)) {
							isValidPage = false;
						} else {
							this.totalSpectraProcessed++;
						}
					}

					//Data chunking management only if the spectrum has not been filtered yet. We also check the interval
					chunked = false;
					if (isValidPage && this.statusDataChunking) {
						if (this.currentCountDataChunking < 1) {
							isValidPage = false;
							chunked = true;
							if (this.currentStepDataChunking === 1) {
								this.currentCountDataChunking = this.countDataChunking + 1;
								this.currentStepDataChunking = this.stepDataChunking + 1;
							}
						}
						this.currentCountDataChunking--;
						this.currentStepDataChunking--;
					}

					//Create spectra
					if (!chunked && isValidPage) {
						spectraStack = this.createSpectraStack(itemSpc, spectraStack);
					}

				//****Manage Many spectrum per file******
				} else if (this.spectrumPerFile > 1 && isValidPage) {

					//Iterate for every spectrum of each page
					for (j = 0; j < typeItems.length; ++j) {
						flag2Spectrum = false;
						itemSpc = aSpectraType.newItem(typeItems[j]);
						twoSpectrumStatus = [];

						//Iterate throw every spectrum which belongs to the stack
						for (k = 0; k < itemSpc.specCount; k++) {

							//Refresh screen and set current spectrum
							itemSpc.update();
							aDoc.update();
							itemSpc.curSpecIndex = k;

							//Reset flags
							filtered = false;
							chunked = false;

							//Data set filtering
							if (this.statusDatasetFilter) {

								//Filter by parameter
								if (this.spectralDataFiltering(itemSpc, this.parameter, this.parameterValue)) {
									filtered = true;
								}

								//Remove spectrum in order to apply the filter properly
								if (filtered) {
									if (itemSpc.specCount > 2) {
										if (itemSpc.curSpecIndex < itemSpc.specCount) {
											if (!(itemSpc.removeSpectrum(itemSpc.curSpecIndex))) {
												MessageBox.warning("Problem with the array filtering at pos: " + k);
											} else {
												k--;
											}
										}
									} else {
										this.manageLast2Spectrum(itemSpc, spcPagesIds, notValidPagesIds, spectraStack, k, twoSpectrumStatus, flag2Spectrum, 0, aSpectraType);
										flag2Spectrum = true;
									}
								}
							}

							//Interval management. We manage the data to first spectrum be the spectrum set by firstDataChunking
							if (!filtered && this.statusDataChunking) {
								totalChunks =  this.numberOfChunks * this.stepDataChunking;
								this.totalSpectra++;
								if (this.totalSpectra < this.firstDataChunking || ((this.totalSpectraProcessed >= totalChunks) && this.statusNumberOfChunks)) {
									if (itemSpc.specCount > 2) {
										if (itemSpc.curSpecIndex < itemSpc.specCount) {
											if (!(itemSpc.removeSpectrum(itemSpc.curSpecIndex))) {
												MessageBox.warning("Problem with the array filtering at pos: " + k);
											} else {
												k--;
											}
										}
									} else {
										this.manageLast2Spectrum(itemSpc, spcPagesIds, notValidPagesIds, spectraStack, k, twoSpectrumStatus, flag2Spectrum, 1, aSpectraType);
										flag2Spectrum = true;
									}
									continue;
								} else {
									this.totalSpectraProcessed++;
								}
							}

							//Data Chunking if the spectrum haven't been filtered yet
							if (this.statusDataChunking && this.stepDataChunking > 1 && !filtered) {
								if (this.currentCountDataChunking < 1) {
									chunked = true;
									if (this.currentStepDataChunking === 1) {
										this.currentCountDataChunking = this.countDataChunking + 1;
										this.currentStepDataChunking = this.stepDataChunking + 1;
									}
								}

								this.currentCountDataChunking--;
								this.currentStepDataChunking--;

								//Remove spectrum in order to apply the data chunking
								if (chunked) {
									if (itemSpc.specCount > 2) {
										if (itemSpc.curSpecIndex < itemSpc.specCount) {
											if (!(itemSpc.removeSpectrum(itemSpc.curSpecIndex))) {
												MessageBox.warning("Problem with the array filtering at pos: " + k);
											} else {
												k--;
											}
										}
									} else {
										this.manageLast2Spectrum(itemSpc, spcPagesIds, notValidPagesIds, spectraStack, k, twoSpectrumStatus, flag2Spectrum, 1, aSpectraType);
										flag2Spectrum = true;
									}
								}
							}
						}

						//Add to the stack if we haven't already been added before by using the manageLast2Spectrum method
						if (!flag2Spectrum) {
							spectraStack = this.createSpectraStack(itemSpc, spectraStack);
						}
					}
				}
			}

			//Decide if each page is valid or not.
			if (isValidPage) {
				spcPagesIds.push(addedPagesIds[i]);
			} else {
				notValidPagesIds.push(addedPagesIds[i]);
			}

			//Update view to perform a full out zoom in order to view the whole spectra stack
			itemSpc.zoom();
			itemSpc.update();
			mainWindow.activeDocument.update();
		}

		//Delte all the page we consider is not valid
		if (notValidPagesIds.length !== 0) {
			aDoc.deletePages(notValidPagesIds);
		}

		//Process every valid page
		if (spcPagesIds.length !== 0) {

			//Now we have to care about if the bunch is formed by only 1 spectra inside the createArrayedItem
			//We don not use any more do actions to change the view of the stack.
			if (spectraStack.length > 0) {
				switch (this.defaultView) {
				case this.ArrayedView:
					this.spectraStackItem = this.createArrayedItem(spectraStack, true, this.spectraStackItem, aDoc, aSpectraType);
					break;
				case this.SuperimposedView:
					this.spectraStackItem = this.createArrayedItem(spectraStack, false, this.spectraStackItem, aDoc, aSpectraType);
					break;
				default:
					break;
				}

				//We avoid the exception if we handle many spectra stack with 1 item length. Fix bug #11382
				//The setProperty method is not implemented yet in the ElViSSpectrum class.
				if (this.spectraStackItem !== undefined && aSpectraType.className === "NMRSpectrum") {
					switch (this.defaultPalette) {
					case this.HuePalette:
						this.spectraStackItem.setProperty(this.dimension === 1 ? "stacked.color.type" : "stacked.2dcolor.type", "Hue");
						break;
					case this.GradientPalette:
						this.spectraStackItem.setProperty(this.dimension === 1 ? "stacked.color.type" : "stacked.2dcolor.type", "Brightness");
						break;
					default:
						break;
					}
				}
			}

			if (this.spectraStack === undefined) {
				aDoc.deletePages(spcPagesIds);
			}

			if (aDoc.curPage().isValid()) {
				this.resultPageId = aDoc.curPage();
				if (aBackupFileName !== undefined && aBackupFileName.length) {
					serialization.save(aBackupFileName, "mnova");
				}
			}
		}

		//Add new page if it is necessary
		if (aDoc.pageCount() === 0) {
			aDoc.newPage();
		}
		Application.mainWindow.processEvents();

		return result;
	}
};

/**************************************************************************************************/

function WImportSpectraStackDialog(aParams) {
	'use strict';

	this.fParams = {};
	this.ui = Application.loadUiFile('ricares:WImportSpectraStackDialog.ui');
	this.on_numberOfChunksChanged(this.ui.widgets.cbNumber.checked);
	this.engine = aParams;
	if (this.ui === undefined || this.ui === null) {
		throw new Error("Unable to load UI");
	}

	//Prepare the group boxes by checking and unchecking them
	this.ui.widgets.gbImportArrrayValues.checked = true;
	this.ui.widgets.gbImportArrrayValues.checked = false;
	this.ui.widgets.fProcessGroupBox.checked = true;
	this.ui.widgets.fProcessGroupBox.checked = false;
	this.ui.widgets.gbBackup.checked = true;
	this.ui.widgets.gbBackup.checked = false;
	this.fParams = aParams;
	this.fParams.metadataParameters = this.processMetadataParameters(this.ui.widgets.cmbParameter);
	this.ui.widgets.fInputDirToolButton.clicked.connect(this, 'on_fInputDirToolButton_clicked');
	this.ui.widgets.fProcessingTemplateToolButton.clicked.connect(this, 'on_fProcessingTemplateToolButton_clicked');
	this.ui.widgets.cbNumber.stateChanged.connect(this, 'on_numberOfChunksChanged');
	this.ui.widgets.fBackupDirToolButton.clicked.connect(this, 'on_fBackupDirToolButton_clicked');
	this.ui.widgets.pbPreviewFiles.clicked.connect(this, 'on_previewFiles');
	this.ui.widgets.fImportArrayValuesToolButton.clicked.connect(this, "on_GetInputArrayDataFile");
	this.ui.accepted.connect(this, 'onAccepted');
	this.updateUI();
}

WImportSpectraStackDialog.prototype = {
	processMetadataParameters : function (aCombobox) {
		'use strict';

		var i, currentItem, parameters, auxItem;

		//Get the object and the real parameter name using the UI name
		parameters = {};
		for (i = 0; i < aCombobox.count; i++) {
			aCombobox.currentIndex = i;
			currentItem = aCombobox.currentText;
			auxItem = currentItem.split(": ");
			parameters[auxItem[0]] = {};
			if (auxItem.length > 1) {
				parameters[auxItem[0]].parameter = auxItem[1].substring(0, auxItem[1].length);
			} else {
				parameters[auxItem[0]].parameter = auxItem[0].toString().toLowerCase();
			}
		}

		//We built again the combobox with generic items, only writing the relevant information
		aCombobox.clear();
		for (currentItem in parameters) {
			if (parameters.hasOwnProperty(currentItem)) {
				aCombobox.addItem(currentItem);
			}
		}

		return parameters;
	},

	exec : function () {
		'use strict';

		var showDialog = true;

		while (showDialog) {

			//Check if cancel button is pulsed by user
			if (!this.ui.exec()) {
				return;
			}

			//Control errors before accept the dialogArguments
			if (this.ui.widgets.fInputDirLineEdit.text === "") {
				MessageBox.critical("You have to set an input directory");
				continue;
			}

			if (!(new Dir(this.ui.widgets.fInputDirLineEdit.text).exists)) {
				MessageBox.critical("The input directory does not exist");
				continue;
			}

			//Check for spectral data filtering
			if (this.ui.widgets.gbBackup.checked && (this.ui.widgets.fBackupDirLineEdit.text === "" || new Dir(this.ui.widgets.fBackupDirLineEdit.text).exists === false)) {
				MessageBox.critical("You have to set a valid backup directory");
				continue;
			}

			if (this.ui.widgets.fProcessGroupBox.checked && (this.ui.widgets.fProcessingTemplateLineEdit.text === "" || new File(this.ui.widgets.fProcessingTemplateLineEdit.text).exists === false)) {
				MessageBox.critical("You have to set a valid processing template file");
				continue;
			}

			if (this.ui.widgets.gbChunking.checked && this.ui.widgets.sbStep.value <= this.ui.widgets.sbCount.value) {
				MessageBox.critical("The Step to Next Chunk value must be greater than Chunk Size value");
				continue;
			}

			//Check for a valid import array data file
			if (this.ui.widgets.gbImportArrrayValues.checked) {
				if (this.ui.widgets.fImportArrayValuesLineEdit.text === undefined || this.ui.widgets.fImportArrayValuesLineEdit.text === "") {
					MessageBox.critical(qsTr("An Import Array File is needed"));
					continue;
				}
				if (!File.exists(this.ui.widgets.fImportArrayValuesLineEdit.text)) {
					MessageBox.critical(qsTr("The Import Array File does not exist"));
					continue;
				} else {
					this.importArrayResult = this.processImportArrayFile(this.ui.widgets.fImportArrayValuesLineEdit.text);
					if (this.importArrayResult.log !== "") {
						MessageBox.critical(qsTr(this.importArrayResult.log));
						continue;
					}
				}
			}

			showDialog = false;
		}
		return true;
	},

	currentDialog : function (aDialog) {
		'use strict';

		this.dialog = aDialog;
	},

	on_numberOfChunksChanged : function (aState) {
		'use strict';

		this.ui.widgets.sbNumber.enabled = false;
		if (aState) {
			this.ui.widgets.sbNumber.enabled = true;
		}
	},

	updateProgressBar : function (aProgressBar, aCurrentValue, aTotalValues, aStartValue, aEndValue) {
		'use strict';

		if (aProgressBar !== undefined) {
			aProgressBar.value = Math.round(aStartValue + ((aCurrentValue / aTotalValues) * (aEndValue - aStartValue)));
			aProgressBar.update();
			mainWindow.processEvents();
		}
	},

	on_previewFiles : function () {
		'use strict';

		var i, inspectDialog, searchData, currentElement, chunkCounter, itemsPerChunkCounter, stepCounter, message,
			filteredFiles, currentMetadata, flagNotFilter, progressFactor, progressBar, auxProgress, numbering;

		//Get current data from GUI
		searchData = {};
		searchData.dir = this.dialog.ui.widgets.fInputDirLineEdit.text;
		searchData.file = this.dialog.ui.widgets.cmbFileNameMasks.currentText;
		searchData.mask = this.dialog.ui.widgets.leFolderNameMasks.currentText;
		searchData.order = this.dialog.ui.widgets.fSortingComboBox.currentIndex;
		searchData.firstFile = this.dialog.ui.widgets.sbFirst.value - 1;
		searchData.chunkSize = this.dialog.ui.widgets.sbCount.value;
		searchData.numberOfChunks = this.dialog.ui.widgets.sbNumber.value;
		searchData.step = this.dialog.ui.widgets.sbStep.value;

		//It warns and avoid a bad user input
		if (!(new Dir(this.dialog.ui.widgets.fInputDirLineEdit.text).exists)) {
			MessageBox.critical("The input directory does not exist");
			return;
		}

		if ((this.dialog.ui.widgets.gbChunking.checked) && ((searchData.step - searchData.chunkSize) < 1)) {
			MessageBox.critical("The Step to Next Chunk value must be greater than Chunk Size value");
			return;
		}

		//Search by using the current GUI data
		progressBar = new ProgressDialog();
		this.engine.getFilesToImport(searchData, progressBar, true);
		filteredFiles = this.engine.filesToBeOpened;

		//Load preview UI
		inspectDialog = Application.loadUiFile("ricares:WImportSpectraStackDialogInspection.ui");
		message = "";

		//Check if progress dialog is needed
		if (this.dialog.ui.widgets.gbDataFiltering.checked && this.dialog.ui.widgets.gbChunking.checked) {
			progressFactor = 0.5;
		} else if (this.dialog.ui.widgets.gbDataFiltering.checked || this.dialog.ui.widgets.gbChunking.checked) {
			progressFactor = 1;
		} else {
			progressFactor = 0;
		}

		//Launch progress dialog if is required
		if (progressFactor > 0) {
			progressBar.labelText = "Getting data from files ...";
			progressBar.visible = true;
		}

		//Manage the preview files taking into account if we have to apply the spectral data algorithm
		if (this.dialog.ui.widgets.gbDataFiltering.checked) {
			filteredFiles = [];
			progressBar.labelText = "Spectral data filtering for preview ...";
			numbering = 1;

			for (i = 0; i < this.engine.filesToBeOpened.length && (progressBar === undefined || !progressBar.wasCanceled); i++) {

				//Update progressBar
				this.updateProgressBar(progressBar, (i + 1), this.engine.filesToBeOpened.length, 0, (100 * progressFactor));

				//Apply data chuking assuming that each file will contain a spectrum
				currentElement = this.engine.filesToBeOpened[i];
				currentMetadata = serialization.metaData(currentElement);
				flagNotFilter = this.engine.metadataFiltering(currentMetadata, this.dialog.ui.widgets.cmbParameter.currentText, this.dialog.ui.widgets.leParameterValue.text);

				//We perform the actions depending on whether is present the data chuking filter
				if (flagNotFilter === true) {
					if (this.dialog.ui.widgets.gbChunking.checked) {
						filteredFiles.push(currentElement);
					} else {
						message += "<font style=\"color: black\">" + numbering + ". " + currentElement + '</font><br>';
						numbering++;
					}
				} else if (flagNotFilter === false) {
					message += "<font style=\"color: gray\">" + currentElement + ' (filtered by spectral data)</font><br>';
				} else {
					message += "<font style=\"color: gray\">" + currentElement + ' (filtered by spectral data due to no metadata has been found)</font><br>';
				}
			}
		}

		//Manage the preview files taking into account if we have to apply the data chuking algorithm
		if (this.dialog.ui.widgets.gbChunking.checked) {

			//Initialize vars
			itemsPerChunkCounter = 0;
			stepCounter = 1;
			chunkCounter = 0;
			auxProgress = progressBar.value;
			progressBar.labelText = "Data chunking filtering for preview ...";

			for (i = 0; i < filteredFiles.length && (progressBar === undefined || !progressBar.wasCanceled); i++) {

				//Update progress bar
				this.updateProgressBar(progressBar, (i + 1), filteredFiles.length, auxProgress, (100 * progressFactor));

				currentElement = filteredFiles[i];
				if (i >= searchData.firstFile) {
					if (chunkCounter < searchData.numberOfChunks || !this.dialog.ui.widgets.cbNumber.checked) {
						if (itemsPerChunkCounter < searchData.chunkSize) {
							message += "<font style=\"color: black\">" + (i + 1) + ". " + currentElement + '</font><br>';
							stepCounter++;
							itemsPerChunkCounter++;
						} else if (stepCounter < searchData.step) {
							message += "<font style=\"color: grey\">" + (i + 1) + ". " + currentElement + ' (filtered by chunking)</font><br>';
							stepCounter++;
						} else {
							message += "<font style=\"color: grey\">" + (i + 1) + ". " + currentElement + ' (filtered by chunking)</font><br>';
							chunkCounter++;
							stepCounter = 1;
							itemsPerChunkCounter = 0;
						}
					} else {
						message += "<font style=\"color: grey\">" + (i + 1) + ". " + currentElement + ' (filtered by chunking)</font> <br>';
					}
				} else {
					message += "<font style=\"color: grey\">" + (i + 1) + ". " + currentElement + ' (filtered by chunking)</font><br>';
				}
			}
		} else {
			progressBar.labelText = "Previewing files ...";
			progressBar.visible = true;
			progressFactor = 1;
			for (i = 0; i < filteredFiles.length && (progressBar === undefined || !progressBar.wasCanceled); i++) {
				this.updateProgressBar(progressBar, (i + 1), filteredFiles.length, auxProgress, (100 * progressFactor));
				currentElement = filteredFiles[i];
				message += "<font style=\"color: black\">" + (i + 1) + ". " + currentElement + '</font><br>';
			}
		}

		//Close progress bar
		progressBar.value = 100;
		progressBar.visible = false;

		//Show text to the user cleaning the ending
		if (message !== "") {
			if (message.substring(message.length - "<br>".length, message.length) === "<br>") {
				message = message.substring(0, message.length - "<br>".length);
			}
			inspectDialog.widgets.txtInspection.html = message;
		}

		inspectDialog.exec();
	},

	getCurrentItemCombobox : function (aCombobox, aText) {
		'use strict';

		var i, result;

		result = -1;
		for (i = 0; i < aCombobox.count; i++) {
			aCombobox.currentIndex = i;
			if (aCombobox.currentText === aText) {
				return i;
			}
		}

		return result;
	},

	updateUI : function () {
		'use strict';

		this.ui.widgets.fInputDirLineEdit.text = this.fParams.inputDir;
		if (this.fParams.historyFiles !== undefined && this.fParams.historyFiles.length > 0) {
			this.ui.widgets.cmbFileNameMasks.clear();
		}
		this.ui.widgets.cmbFileNameMasks.items = this.fParams.historyFiles;
		this.ui.widgets.cmbFileNameMasks.currentItem = this.getCurrentItemCombobox(this.ui.widgets.cmbFileNameMasks, this.fParams.currentFile);
		this.ui.widgets.fSortingComboBox.currentIndex = this.fParams.defaultSorting;
		this.ui.widgets.fDefaultViewComboBox.currentIndex = this.fParams.defaultView;
		this.ui.widgets.fPaletteComboBox.currentIndex = this.fParams.defaultPalette;
		this.ui.widgets.fVisualDecimationSpinBox.value = this.fParams.visualDecimation;
		this.ui.widgets.cmbParameter.currentText = this.fParams.parameter;
		this.ui.widgets.leParameterValue.text = this.fParams.parameterValue;
		this.ui.widgets.gbChunking.checked = this.fParams.statusDataChunking;
		this.ui.widgets.sbFirst.value = this.fParams.firstDataChunking;
		this.ui.widgets.sbNumber.value = this.fParams.numberOfChunks;
		this.ui.widgets.cbNumber.checked = this.fParams.statusNumberOfChunks;
		this.ui.widgets.sbStep.value = this.fParams.stepDataChunking;
		this.ui.widgets.sbCount.value = this.fParams.countDataChunking;
		this.ui.widgets.gbImportArrrayValues.checked = this.fParams.doImportArrayValues;
		this.ui.widgets.fImportArrayValuesLineEdit.text = this.fParams.importArrayValuesFile;
		this.ui.widgets.fBackupDirLineEdit.text = this.fParams.backupDir;
		this.ui.widgets.fProcessingTemplateLineEdit.text = this.fParams.fProcessingTemplate;
		this.ui.widgets.gbDataFiltering.checked = this.fParams.statusDatasetFilter;
		this.ui.widgets.fProcessGroupBox.checked = this.fParams.statusProcessingTemplate;
		this.ui.widgets.gbBackup.checked = this.fParams.doBackup;
		this.ui.widgets.leFolderNameMasks.clear();
		this.ui.widgets.leFolderNameMasks.items = this.fParams.historyMasks;
		this.ui.widgets.leFolderNameMasks.currentItem = this.getCurrentItemCombobox(this.ui.widgets.leFolderNameMasks, this.fParams.currentMask);
	},

	result : function () {
		'use strict';

		return this.fParams;
	},

	onAccepted : function () {
		'use strict';

		this.fParams.inputDir = this.ui.widgets.fInputDirLineEdit.text;
		this.fParams.currentFile = this.ui.widgets.cmbFileNameMasks.currentText;
		if (this.fParams.historyFiles.length >= this.ui.widgets.cmbFileNameMasks.maxCount) {
			this.fParams.historyFiles.shift();
		} else if (this.fParams.historyFiles.length === 0) {
			this.fParams.historyFiles = this.ui.widgets.cmbFileNameMasks.items;
		}
		if (this.fParams.currentFile !== "" && this.getCurrentItemCombobox(this.ui.widgets.cmbFileNameMasks, this.fParams.currentFile) === -1) {
			this.fParams.historyFiles.push(this.fParams.currentFile);
		}
		this.fParams.defaultSorting = this.ui.widgets.fSortingComboBox.currentIndex;
		this.fParams.defaultView = this.ui.widgets.fDefaultViewComboBox.currentIndex;
		this.fParams.defaultPalette = this.ui.widgets.fPaletteComboBox.currentIndex;
		this.fParams.visualDecimation = this.ui.widgets.fVisualDecimationSpinBox.value;
		this.fParams.parameter = this.ui.widgets.cmbParameter.currentText;
		this.fParams.parameterValue = this.ui.widgets.leParameterValue.text;
		this.fParams.statusDataChunking = this.ui.widgets.gbChunking.checked;
		this.fParams.firstDataChunking = this.ui.widgets.sbFirst.value;
		this.fParams.numberOfChunks = this.ui.widgets.sbNumber.value;
		this.fParams.statusNumberOfChunks = this.ui.widgets.cbNumber.checked;
		this.fParams.stepDataChunking = this.ui.widgets.sbStep.value;
		this.fParams.countDataChunking = this.ui.widgets.sbCount.value;
		this.fParams.doImportArrayValues = this.ui.widgets.gbImportArrrayValues.checked;
		this.fParams.importArrayValuesFile = this.ui.widgets.fImportArrayValuesLineEdit.text;
		this.fParams.statusDatasetFilter = this.ui.widgets.gbDataFiltering.checked;
		this.fParams.statusProcessingTemplate = this.ui.widgets.fProcessGroupBox.checked;
		this.fParams.doBackup = this.ui.widgets.gbBackup.checked;
		this.fParams.currentMask = this.ui.widgets.leFolderNameMasks.currentText;
		if (this.fParams.historyMasks.length >= this.ui.widgets.leFolderNameMasks.maxCount) {
			this.fParams.historyMasks.shift();
		} else if (this.fParams.historyMasks.length === 0) {
			this.fParams.historyMasks = this.ui.widgets.leFolderNameMasks.items;
		}
		if (this.getCurrentItemCombobox(this.ui.widgets.leFolderNameMasks, this.fParams.currentMask) === -1) {
			this.fParams.historyMasks.push(this.fParams.currentMask);
		}
		this.fParams.backupDir = this.ui.widgets.fBackupDirLineEdit.text;
		this.fParams.fProcessingTemplate = this.ui.widgets.fProcessingTemplateLineEdit.text;
	},

	on_fInputDirToolButton_clicked : function () {
		'use strict';
		var dir, dirPath;
		dir = this.fParams.inputDir;
		if (dir.length === 0) {
			dir = Dir.home();
		}
		dirPath = FileDialog.getExistingDirectory(dir, "Select Directory");
		if (dirPath.length) {
			this.fParams.inputDir = dirPath;
			this.ui.widgets.fInputDirLineEdit.text = this.fParams.inputDir;
		}
	},

	on_fProcessingTemplateToolButton_clicked : function () {
		'use strict';
		var dir, filePath;

		dir = this.ui.widgets.fProcessingTemplateLineEdit.text;
		if (dir.length === 0) {
			dir = Dir.home();
		}
		filePath = FileDialog.getOpenFileName("Mnova Processing Template (*.mnp)", "Select a Processing Template", dir);
		if (filePath.length) {
			this.ui.widgets.fProcessingTemplateLineEdit.text = filePath;
		}
	},

	on_fBackupDirToolButton_clicked : function () {
		'use strict';

		var dir, dirPath;

		dir = this.ui.widgets.fBackupDirLineEdit.text;
		if (dir.length === 0) {
			dir = Dir.tmp();
		}
		dirPath = FileDialog.getExistingDirectory(dir, "Select Directory");
		if (dirPath.length) {
			this.ui.widgets.fBackupDirLineEdit.text = dirPath;
		}
	},

	on_GetInputArrayDataFile : function () {
		'use strict';

		var dir, filePath;

		dir = this.ui.widgets.fImportArrayValuesLineEdit.text;
		if (dir.length === 0) {
			dir = Dir.home();
		}
		filePath = FileDialog.getOpenFileName("", "Select a Import Array Data File", dir);
		if (filePath.length) {
			this.ui.widgets.fImportArrayValuesLineEdit.text = filePath;
		}
	},

	processImportArrayFile : function (aFilePath) {
		'use strict';
		var i, file, stream, auxData, data, result, line;

		result = {};
		result.log = "";
		result.data = [];

		try {
			file = new File(aFilePath);
			if (!file.exists) {
				result.log = "The Import Array File does not exist";
				return result;
			}
			file.open(File.ReadOnly);
			stream = new TextStream(file);
			stream.codec = "UTF-8";
			auxData = stream.readAll();
			stream.flush();
			file.close();
			data = auxData.split(/\r\n|\n/g);
		} catch (e) {
			result.log = e;
		}

		for (i = 0; i < data.length; i++) {
			line = data[i];
			if (line !== "") {
				if (isNaN(line)) {
					result.log = "Wrong Import Array File.\nThe content of the line " + (i + 1) + " is not a valid number";
					break;
				} else {
					result.data.push(line);
				}
			} else {
				//We only allow empty the last line
				if (i !== (data.length - 1)) {
					result.log = "Wrong Import Array File.\nThe content of the line " + (i + 1) + " is empty";
				}
			}
		}

		return result;
	},

	updateImportArrayedData : function (aSpectrum, aData) {
		"use strict";

		var i, auxArray, arrTable;

		auxArray =  aSpectrum.arrayedData;
		for (i = 0; i < aSpectrum.specCount; i++) {
			if (i < aData.length) {
				auxArray[i] = aData[i];
			}
		}
		aSpectrum.arrayedData = auxArray;
		arrTable = mainWindow.getObject("NMRArrayedTable");
		if (arrTable) {
			arrTable.updateData();
		}
	}
};

if (this.MnUi && MnUi.scripts_common) {
	MnUi.scripts_common.scripts_common_ImportDirectorySpectraStack = spectraStackImporter;
}

